# -*- coding: utf-8 -*-
"""
Doc: service.json内配置

Created on 2016/1/6
@author: MT
"""
import json
import os

# from app.const.enums.server_info import LockInfo, SessionEncrypt
from app.const.enums.server_info import SessionEncrypt
from app.const.lang.tips_info import TipsInfo
from framework.firefly_dc.globalobject import GlobalObject
from utils.dict_utils import dict_merge
from utils.print_utils import echo
from utils.time_utils import getCT


def init(service_json_path='service.json', base_service_json_path='base_service.json'):
    """
    初始化config
    :param service_json_path: 自定义service.json文件路径
    :param base_service_json_path: 默认service.json文件路径
    """
    global config
    try:
        base_config = json.load(open(base_service_json_path))
    except Exception as e:
        _config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), r'base_service.json')
        base_config = json.load(open(_config_path))

    try:
        service_config = json.load(open(service_json_path))
    except Exception as e:
        try:
            echo("1 service.json not exist  ", service_json_path, ' ', e.message)
            _config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'service.json')
            service_config = json.load(open(_config_path))

        except Exception as e:
            echo("2 service.json not exist", _config_path, ' ', e.message)
            service_config = {}

    config = dict_merge(base_config, service_config)


def update_by_zk_service_config(zk_service_config, doing_bootstrap=False):
    """
    根据从zookeeper获取的配置数据进行更新

    .. versionchanged:: 1.0
       增加配置修改时`profile_version`值变更更新配置

    :param zk_service_config: dict
    :param doing_bootstrap: 是否启动时调用
    """
    if ServerRunMode.is_standalone_mode():
        return

    # 仅在启动进程时更新值,运行期间不再改变
    bootstrap_config_list = [
        # 数据库配置(主要是配置库)
        'dbconf',
        # redis配置
        'cacheconf',
        # 数据库访问模式
        'dal_mode',
        # 定时任务运行模式
        'sched_mode',
        # 行为日志写入模式
        'run_log_mode',
        # 接口日志代理模式
        'log_proxy_mode',
        # zk地址
        'zookeeper_host_list',
        # flume的zk地址
        'flume_zookeeper_host_list',
        # flume的zk节点路径
        'flume_zookeeper_path',
        # 非核心日志配置
        'non_core_log_config',
        'non_core_log_config_all',
        # 非核心日志代理模式
        'non_core_log_mode',
    ]

    from common import server_config
    if server_config.is_debug:
        bootstrap_config_list.append('serverid')

    # 启动进程时更新值,运行期间也可以实时改变
    runtime_config_list = [
        # rpc异常重试次数
        'rpc_exception_retry_times',
        # 远端服务地址
        'remoteservices',
        # 服务开关
        'service_switch',
        # 白名单
        'ip_white_list',
        # 热云appid
        'reyun_appid',
        # 调试日志开关
        'log_switch',
        # 模块开关
        'module_switch',
        # 版本控制开关
        'version_control',
        # 默认锁超时时间
        'lock_default_overtime',
        # tb配置缓存拉取间隔（秒）
        'refresh_tb_profile_cache_cd',
        # 会话加密相关配置
        'session_encrypt_cfg',
        # chaos orm 临时表字段长度
        'orm_char_temporary_long_cfg',
        # AdminToken
        'admin_token',
        # 个人邮件限制，值 {'limit_num': 0, 'limit_time' 86400}
        'single_mail_limit_cfg'
    ]

    if doing_bootstrap:
        for cfg_key in bootstrap_config_list + runtime_config_list:
            if cfg_key in zk_service_config:
                config[cfg_key] = zk_service_config[cfg_key]

    for use_zk_config in runtime_config_list:
        if use_zk_config in zk_service_config:
            config[use_zk_config] = zk_service_config[use_zk_config]

    # 更新锁超时时间
    # _update_lock_default_overtime()

    # 接口拦截器数据
    action_switch = ActionSwitch.action_switch()

    config['action_switch'] = action_switch

    # 日志开关数据
    # from pyshare.framework.monitor import MonitorManager
    # MonitorManager.format_log_switch(config)


# 获取service.json配置 通过 config  如: config['servers']['frontend']['webport']
config = GlobalObject.global_config

if not config:
    init()


class ActionSwitch(object):
    """
    接口拦截器
    """

    # 单接口/区服 区间长度
    S_LENGTH = 1
    # 多接口/区服 区间长度
    M_LENGTH = 2
    # 区服控制key
    SERVER = "server_id"

    @classmethod
    def get_range_dict(cls, range_list):
        """
        将区间字符串转成列表(区服区间/接口区间)
        :param range_list: 区间字符串: ["1", "4-10", "20"]
        :return:
        """
        xrange_dict = {}
        for server_range in range_list:
            range_length = server_range.split("-")
            if len(range_length) == cls.S_LENGTH:
                range_list = xrange(int(range_length[0]), int(range_length[0]) + 1)
            elif len(range_length) == cls.M_LENGTH:
                range_list = xrange(int(range_length[0]), int(range_length[1]) + 1)
            else:
                continue
            for key in range_list:
                xrange_dict.update({str(key): 0})
        return xrange_dict

    @classmethod
    def action_switch(cls):
        """
        返回被拦截的区服id和相关的接口id、已经对应的文本提示
        :return {区服id: {接口id: 提示文本}}
        """
        action_forbid_list = []
        if 'module_switch' in config:
            for switch in config['module_switch'].values():
                # 接口拦截器相关数据
                action_dict = dict()
                # 获取拦截的区服
                new_switch = switch.copy()
                try:
                    if cls.SERVER in switch:
                        server_list = switch.get(cls.SERVER).split(",")
                        del new_switch[cls.SERVER]
                    else:
                        server_list = []
                    text = new_switch.values()[0]

                    if not text:
                        text = TipsInfo.GlobalKeys.actionUnavailable
                except:
                    text = TipsInfo.GlobalKeys.actionUnavailable
                    server_list = []

                action_list = new_switch.keys()[0].split(",")
                # 转换拦截的区服成列表形式
                action_dict["server_id"] = cls.get_range_dict(server_list)
                # 转换拦截的接口成列表形式
                action_dict["action_id"] = cls.get_range_dict(action_list)
                action_dict["tips"] = text
                action_forbid_list.append(action_dict)

        return action_forbid_list


class ServerRunMode(object):
    """
    服务器运行模式
    对应service.json中的server_run_mode
    """
    # 单机模式
    STANDALONE = 'standalone'
    # 集群模式
    CLUSTER = 'cluster'

    @classmethod
    def get_server_run_mode(cls):
        """
        返回服务器运行模式
        """
        global config
        return config.get('server_run_mode', cls.STANDALONE)

    @classmethod
    def is_standalone_mode(cls):
        """是否以单机模式运行"""
        return cls.get_server_run_mode() == cls.STANDALONE

    @classmethod
    def is_cluster_mode(cls):
        """是否以集群模式运行"""
        return cls.get_server_run_mode() == cls.CLUSTER


class SchedMode(object):
    """
    定时任务运行模式
    """
    # 本地模式
    LOCAL_MODE = 'local'
    # 远端模式
    REMOTE_MODE = 'remote'

    @classmethod
    def get_sched_mode(cls):
        """返回定时任务运行模式"""
        global config
        return config['sched_mode']

    @classmethod
    def is_local_mode(cls):
        """是否本地模式"""
        return cls.get_sched_mode() == cls.LOCAL_MODE

    @classmethod
    def is_remote_mode(cls):
        """是否远端模式"""
        return cls.get_sched_mode() == cls.REMOTE_MODE


class ActionLogProxyMode(object):
    """
    接口日志代理模式
    对应service.json中的log_mode
    """
    # flume
    FLUME = 'flume'
    # 不记录
    DUMMY = 'dummy'

    @classmethod
    def get_action_log_proxy_mode(cls):
        """
        返回接口日志代理模式
        """
        global config
        return config.get('log_proxy_mode', cls.DUMMY)

    @classmethod
    def is_flume_mode(cls):
        return cls.get_action_log_proxy_mode() == cls.FLUME

    @classmethod
    def is_dummy_mode(cls):
        return cls.get_action_log_proxy_mode() == cls.DUMMY


class NonCoreLogProxyMode(object):
    """
    非核心接口日志代理模式
    对应service.json中的non_core_log_mode
    """
    # nfs
    NFS = 'nfs'
    # 不记录
    DUMMY = 'dummy'

    @classmethod
    def get_action_log_proxy_mode(cls):
        """
        返回接口日志代理模式
        """
        global config
        return config.get('non_core_log_mode', cls.DUMMY)

    @classmethod
    def is_nfs_mode(cls):
        return cls.get_action_log_proxy_mode() == cls.NFS

    @classmethod
    def is_dummy_mode(cls):
        return cls.get_action_log_proxy_mode() == cls.DUMMY


class DalMode(object):
    """
    数据访问层模式
    对应service.json中的dal_mode
    """
    # 使用peewee ORM
    PEEWEE = 'peewee'
    # 使用chaos_orm
    CHAOS = 'chaos'
    # 使用peewee orm sqlite
    SQLITE = 'sqlite'

    @classmethod
    def get_dal_mode(cls):
        """
        返回数据访问层模式
        """
        global config
        return config.get('dal_mode', cls.PEEWEE)

    @classmethod
    def is_peewee_mode(cls):
        return cls.get_dal_mode() == cls.PEEWEE

    @classmethod
    def is_chaos_mode(cls):
        return cls.get_dal_mode() == cls.CHAOS

    @classmethod
    def is_sqlite_mode(cls):
        return cls.get_dal_mode() == cls.SQLITE


def get_lock_default_overtime():
    """
    获取锁默认超时时间
    :return:
    """
    global config
    return config.get("lock_default_overtime", 60)


class RunLogMode(object):
    """
    运行日志记录模式
    对应service.json中的run_log_mode
    取值为：
    rsyslog -> 只写到rsyslog
    "" -> 不写日志
    """
    # 通过Rsyslog转发
    LOG_RSYSLOG = 'rsyslog'

    @classmethod
    def is_use_log_rsyslog(cls):
        global config
        return cls.LOG_RSYSLOG == config['run_log_mode']


def get_action_id():
    """
    获取接口id
    :return:
    """
    from framework.firefly_dc.context import action_context_storage

    if action_context_storage.ctx is not None and action_context_storage.ctx.request is not None:
        return action_context_storage.ctx.request.action_id
    return '0'


def get_app_key():
    """
    获取app_key

    :rtype: str
    """
    from framework.firefly_dc.context import action_context_storage

    if action_context_storage.ctx is not None:
        return action_context_storage.ctx.app_key
    return ''


def get_client_ip():
    """
    从请求上下文获取到客户端ip
    :rtype: str
    """
    from framework.firefly_dc.context import action_context_storage
    if action_context_storage.ctx is not None:
        return action_context_storage.ctx.ip
    return ''


def get_retail_id():
    """
    从请求上下文获取到渠道号
    :rtype: int
    """
    from framework.firefly_dc.context import action_context_storage
    if action_context_storage.ctx is not None:
        return action_context_storage.ctx.retail_id
    return 0


def get_activity_id():
    """
    从请求上下文获取到活动id
    :rtype: str
    """
    from framework.firefly_dc.context import action_context_storage
    if action_context_storage.ctx is not None:
        field_id = action_context_storage.ctx.request.args.get("fieldid")
        if field_id:
            return field_id
        return action_context_storage.ctx.activity_id
    return ''


def get_ctx_time():
    """
    从上下文中获取当前时间
    :rtype: datetime
    """
    from framework.firefly_dc.context import action_context_storage
    if action_context_storage.ctx is not None:
        return action_context_storage.ctx.current_time
    return getCT()


def get_role_guid():
    """
    从上下文中获取玩家ID
    :return:
    """
    from framework.firefly_dc.context import action_context_storage
    if action_context_storage.ctx is not None:
        return action_context_storage.ctx.rid
    return ''


def get_progress_list():
    """
    从上下文中获取资源使用情况
    :return:
    """
    from framework.firefly_dc.context import action_context_storage
    if action_context_storage.ctx is not None:
        return action_context_storage.ctx.progress_list
    return []


# 环境变量中的APP_ID值
env_app_id = os.getenv('APP_ID', default=None)
env_app_id = int(env_app_id) if env_app_id is not None else 1010


def get_game_id():
    """
    获取gameid
    """
    # 万岁爷的gameid是104
    # :)
    return 104


# def get_remote_service_switch(switch_id):
#     """
#     获取远程服务开关
#     update 3.1 优化开关
#     :param switch_id: 服务开关
#     :return:
#     """
#     return get_service_switch(switch_id)


# def get_service_switch(switch_id):
#     """
#     获取zk开关
#     :param switch_id: 开关id
#     :return:
#     """
#     from pyshare.const.enums.remote_service_switch import SwitchIdToKeyMap, ZkControlDefine
#     global config
#
#     service_switch = config.get("service_switch", {})
#
#     # 根据id获取开关定义对象，取不到使用默认定义，是为了遵循原逻辑，未定义的返回True
#     cf_define = SwitchIdToKeyMap.get(switch_id, ZkControlDefine.Default)
#     return service_switch.get(cf_define['zk_key'], cf_define['default'])


# def get_all_zk_switch():
#     """
#     获取所有zk开关，用于后台管理显示
#     :return:[{
#         "zk_key": str,
#         "default": bool,
#         "desc_str": str,
#         "zk_value": bool,
#         "switch_id": int
#     }]
#     """
#     from pyshare.const.enums.remote_service_switch import SwitchIdToKeyMap, ZkControlDefine
#     global config
#     from copy import deepcopy
#
#     service_switch = config.get("service_switch", {})
#
#     return_list = []
#     for switch_id, zk_control_define in SwitchIdToKeyMap.iteritems():
#         d = deepcopy(zk_control_define)
#         d['zk_value'] = 1 if service_switch.get(zk_control_define['zk_key'], zk_control_define['default']) else 0
#         d['switch_id'] = switch_id
#         return_list.append(d)
#     return return_list


class LogSwitchParams(object):
    """特殊日志控制参数"""

    #########################
    # 远端调用耗时日志
    #########################
    @classmethod
    def is_open_remote_service_call_time_log(cls):
        """是否开启远端服务调用耗时日志"""
        return True if config['log_switch']['remote_server_run_time'] else False

    @classmethod
    def remote_service_call_time_log_minimum_cost_ms(cls):
        """记录远端调用服务的最低耗时时长(ms)"""
        return config['log_switch']['log_minimum_cost_ms']

    #########################
    # 锁异常日志
    #########################
    @classmethod
    def mutex_spin_log_minimum_cost_ms(cls):
        """记录锁自旋最低耗时时长(ms) """
        return config['log_switch']['log_minimum_mutex_spin_cost_ms']


class VersionControl(object):
    """版本控制参数"""
    # 默认服务端版本号
    DEFAULT_SERVER_VERSION = 2.2
    # 默认最低兼容客户端版本号
    DEFAULT_MIN_COMPATIBLE_CLIENT_VERSION = 2.1

    @classmethod
    def check_client_ver(cls):
        """是否进行客户端版本参数检查"""
        vc = config.get('version_control', None)
        return vc.get('check_client_ver', True) if vc is not None else True

    @classmethod
    def server_version(cls):
        """服务端版本号"""
        vc = config.get('version_control', None)
        return vc.get('server_version', cls.DEFAULT_SERVER_VERSION) \
            if vc is not None else cls.DEFAULT_SERVER_VERSION

    @classmethod
    def min_compatible_client_ver(cls):
        """兼容的最低客户端版本号"""
        vc = config.get('version_control', None)
        return vc.get('min_compatible_client_ver', cls.DEFAULT_MIN_COMPATIBLE_CLIENT_VERSION) \
            if vc is not None else cls.DEFAULT_MIN_COMPATIBLE_CLIENT_VERSION


# 进程启动时是否启动定时任务执行线程
_run_sched_executor = bool(os.getenv('RUN_SCHED_EXECUTOR', True))


def whether_run_sched_executor():
    """进程启动时是否启动定时任务执行线程(仅集群模式下有效,单机模式必定启动)"""
    if ServerRunMode.is_cluster_mode():
        return _run_sched_executor
    return True


# def get_lock_default_overtime():
#     """
#     获取锁默认超时时间
#     :return:
#     """
#     global config
#     return config.get("lock_default_overtime", LockInfo.DefaultOvertime)


# def _update_lock_default_overtime():
#     """
#     更新锁默认超时时间
#     :return:
#     """
#     from pyshare.framework.distribute.mutex import Mutex
#     global config
#     Mutex.DEFAULT_OVERTIME = config.get("lock_default_overtime", LockInfo.DefaultOvertime)


def get_session_encrypt_cfg():
    """
    获取关于会话加密的配置
    :return:
    """
    global config
    # 默认配置
    default_cfg = {
        'encrypt_type': SessionEncrypt.DefaultEncrypt,  # 使用时间戳拼接加密
        'interval_second': SessionEncrypt.DefaultIntervalSecond  # 间隔时间300秒
    }
    return config.get('session_encrypt_cfg', default_cfg)


def get_orm_model_tem_long(table_name, filed_name):
    """
    获取zk上设置的临时字段长度，用于临时解决字段超长问题；
    代码修复可在下次更新时修复，修复后可删除zk配置
    orm_char_temporary_long_cfg：{'table_name': {'field_name_1': 100}}
    """
    global config
    return config.get('orm_char_temporary_long_cfg', {}).get(table_name, {}).get(filed_name, None)


def get_admin_token_cfg():
    """
    获取zk上的admin token 配置

    """
    global config
    return config.get('admin_token', False)


def get_single_mail_limit_cfg():
    """
    获取zk上设置的个人邮件阈值限制，用于拦截角色获取【同标题、内容、奖励的邮件】
    single_mail_limit_cfg: {'limit_num': 0, 'limit_time' 86400}
    limit_num: 0（默认值）表示不进行限制, 1表示对于同一封邮件（标题、内容、奖励）只允许获得一封， ...
    limit_time: 86400（单位 秒， 默认值） 表示同一封邮件限制效果的持续时间
    """
    global config
    limit_cfg = config.get('single_mail_limit_cfg', {})
    limit_num = limit_cfg.get('limit_num', 0)  # 默认不限制
    limit_time = limit_cfg.get('limit_time', 86400)  # 默认1天
    return limit_num, limit_time
